package coc.core.util;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.joda.time.DateTime;

import coo.base.exception.UncheckedException;
import coo.base.util.DateUtils;
import coo.base.util.StringUtils;

/**
 * 日期拾取器。
 */
public class DatePicker {
	/** 内置支持的日期格式。排列的顺序：长的格式应放在前面，短的格式放在后面。 */
	private static final String[] DATE_FORMATS = new String[] {
			"yyyy-MM-dd HH:mm:ss", "yy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm",
			"yy-MM-dd HH:mm", "yyyy-MM-dd", "yy-MM-dd", "MM-dd HH:mm:ss",
			"MM-dd HH:mm", "MM-dd", "yyyy年MM月dd日 HH:mm:ss",
			"yy年MM月dd日 HH:mm:ss", "yyyy年MM月dd日HH:mm:ss", "yy年MM月dd日HH:mm:ss",
			"yyyy年MM月dd日 HH:mm", "yy年MM月dd日 HH:mm", "yyyy年MM月dd日HH:mm",
			"yy年MM月dd日HH:mm", "yyyy年MM月dd日", "yy年MM月dd日", "MM月dd日 HH:mm:ss",
			"MM月dd日HH:mm:ss", "MM月dd日 HH:mm", "MM月dd日HH:mm", "MM月dd日",
			"dd/MM/yyyy HH:mm:ss", "dd/MM/yy HH:mm:ss", "dd/MM/yyyy HH:mm",
			"dd/MM/yy HH:mm", "dd/MM/yyyy", "dd/MM/yy", "dd/MM HH:mm:ss",
			"dd/MM HH:mm", "dd/MM" };

	/**
	 * 从字符串中获取第1个日期时间。
	 * 
	 * @param dateStr
	 *            字符串
	 * @return 返回从字符串中解析出来的日期时间。
	 */
	public static Date get(String dateStr) {
		return get(dateStr, 0);
	}

	/**
	 * 从字符串中获取第N个日期时间。
	 * 
	 * @param dateStr
	 *            字符串
	 * @param n
	 *            第N个
	 * @return 返回从字符串中解析出来的日期时间。
	 */
	public static Date get(String dateStr, Integer n) {
		List<Date> dates = getDates(dateStr);
		if (dates.isEmpty()) {
			return null;
		}
		return dates.get(n);
	}

	/**
	 * 从字符串中获取第1个指定格式的日期时间。
	 * 
	 * @param dateStr
	 *            字符串
	 * @param format
	 *            日期时间格式
	 * @return 返回从字符串中解析出来的日期时间。
	 */
	public static Date get(String dateStr, String format) {
		return get(dateStr, format, 0);
	}

	/**
	 * 从字符串中获取第N个指定格式的日期时间。
	 * 
	 * @param dateStr
	 *            字符串
	 * @param format
	 *            日期时间格式
	 * @param n
	 *            第N个
	 * @return 返回从字符串中解析出来的日期时间。
	 */
	public static Date get(String dateStr, String format, Integer n) {
		List<Date> dates = getDates(dateStr, format);
		if (dates.isEmpty()) {
			throw new UncheckedException("无法用表达式[" + format + "]解析[" + dateStr
					+ "]中的日期。");
		}
		return dates.get(n);
	}

	/**
	 * 从字符串中获取指定格式的所有日期时间。
	 * 
	 * @param dateStr
	 *            字符串
	 * @param format
	 *            日期时间格式
	 * @return 返回从字符串中解析出来的日期时间列表。
	 */
	private static List<Date> getDates(String dateStr, String format) {
		List<Date> dates = new ArrayList<Date>();
		if (StringUtils.isBlank(dateStr)) {
			return dates;
		}
		for (FixedTextDates fixedTextDate : FixedTextDates.values()) {
			dateStr = fixedTextDate.replace(dateStr);
		}
		for (FloatTextDates floatTextDate : FloatTextDates.values()) {
			dateStr = floatTextDate.replace(dateStr);
		}
		Pattern pattern = Pattern.compile(toRegex(format));
		Matcher matcher = pattern.matcher(dateStr);
		while (matcher.find()) {
			try {
				dates.add(DateUtils.parse(matcher.group(), format));
			} catch (UncheckedException e) {
				e.getMessage();
			}
		}
		return dates;
	}

	/**
	 * 从字符串中获取所有日期时间。
	 * 
	 * @param dateStr
	 *            字符串
	 * @return 返回从字符串中解析出来的日期时间列表。
	 */
	private static List<Date> getDates(String dateStr) {
		List<Date> dates = new ArrayList<Date>();
		if (StringUtils.isBlank(dateStr)) {
			return dates;
		}
		for (FixedTextDates fixedTextDate : FixedTextDates.values()) {
			dateStr = fixedTextDate.replace(dateStr);
		}
		for (FloatTextDates floatTextDate : FloatTextDates.values()) {
			dateStr = floatTextDate.replace(dateStr);
		}

		List<String> regexs = new ArrayList<String>();
		for (String format : DATE_FORMATS) {
			regexs.add(toRegex(format));
		}
		Pattern pattern = Pattern.compile(StringUtils.join(regexs, "|"));
		Matcher matcher = pattern.matcher(dateStr);

		while (matcher.find()) {
			dates.add(getDate(matcher.group()));
		}
		return dates;
	}

	/**
	 * 将日期时间字符串（不包含其它字符）转换成日期时间对象。转换时遍历内置支持的格式，返回第一个匹配解析成功的日期时间对象。
	 * 
	 * @param dateStr
	 *            日期时间字符串
	 * @return 返回转换后的日期时间对象。
	 */
	private static Date getDate(String dateStr) {
		for (String format : DATE_FORMATS) {
			try {
				return DateUtils.parse(dateStr, format);
			} catch (UncheckedException e) {
				e.getMessage();
			}
		}
		throw new UncheckedException("无法解析[" + dateStr + "]中的日期。");
	}

	/**
	 * 将日期时间格式转换成对应的正则表达式。
	 * 
	 * @param format
	 *            日期时间格式
	 * @return 返回日期时间格式转换成对应的正则表达式。
	 */
	private static String toRegex(String format) {
		format = format.replaceAll("y", "\\\\d{1}");
		format = format.replaceAll("MM", "\\\\d{1,2}");
		format = format.replaceAll("mm", "\\\\d{1,2}");
		format = format.replaceAll("dd", "\\\\d{1,2}");
		format = format.replaceAll("HH", "\\\\d{1,2}");
		format = format.replaceAll("hh", "\\\\d{1,2}");
		format = format.replaceAll("ss", "\\\\d{1,2}");
		return format;
	}

	/**
	 * 固定文本日期时间枚举类。
	 */
	private enum FixedTextDates {
		TODAY("今天", 0, DateUtils.DAY), YESTERDAY("昨天", 24 * 60, DateUtils.DAY), BEFORE_YESTERDAY(
				"前天", 2 * 24 * 60, DateUtils.DAY), QUARTER("一刻钟前", 15,
				DateUtils.SECOND), HALFHOUR("半小时前", 30, DateUtils.SECOND);

		private String regex;
		private Integer minusMinute;
		private String format;

		/**
		 * 构造方法。
		 * 
		 * @param regex
		 *            正则表达式
		 * @param minusMinute
		 *            时间减量（分钟）
		 * @param format
		 *            输出的日期格式
		 */
		private FixedTextDates(String regex, Integer minusMinute, String format) {
			this.regex = regex;
			this.minusMinute = minusMinute;
			this.format = format;
		}

		/**
		 * 将字符串中的日期时间相关的固定文本替换成日期时间字符串。
		 * 
		 * @param dateStr
		 *            字符串
		 * @return 返回替换后的字符串。
		 */
		public String replace(String dateStr) {
			DateTime dateTime = new DateTime();
			dateTime = dateTime.minusMinutes(minusMinute);
			return dateStr.replaceAll(regex,
					DateUtils.format(dateTime.toDate(), format));
		}
	}

	/**
	 * 浮动文本日期时间枚举类。
	 */
	private enum FloatTextDates {
		SECONDS("\\d{1,2}\\s*秒前", 1, DateUtils.SECOND), MINUTES(
				"\\d{1,2}\\s*分钟前", 60, DateUtils.SECOND), HOURS(
				"\\d{1,2}\\s*小时前", 60 * 60, DateUtils.SECOND), DAYS(
				"\\d+\\s*天前", 24 * 60 * 60, DateUtils.DAY);

		private String regex;
		private Integer minusSeconds;
		private String format;

		/**
		 * 构造方法。
		 * 
		 * @param regex
		 *            正则表达式
		 * @param minusSeconds
		 *            每单位时间减量（秒钟）
		 * @param format
		 *            输出的日期格式
		 */
		private FloatTextDates(String regex, Integer minusSeconds, String format) {
			this.regex = regex;
			this.minusSeconds = minusSeconds;
			this.format = format;
		}

		/**
		 * 将字符串中的日期时间相关的固定文本替换成日期时间字符串。
		 * 
		 * @param dateStr
		 *            字符串
		 * @return 返回替换后的字符串。
		 */
		public String replace(String dateStr) {
			Pattern pattern = Pattern.compile(regex);
			Matcher matcher = pattern.matcher(dateStr);
			while (matcher.find()) {
				String group = matcher.group();
				Integer quantity = Integer
						.parseInt(group.replaceAll("\\D", ""));
				DateTime dateTime = new DateTime();
				dateTime = dateTime.minusSeconds(quantity * minusSeconds);
				dateStr = dateStr.replace(group,
						DateUtils.format(dateTime.toDate(), format));
			}
			return dateStr;
		}
	}
}
